package ci.web.core;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;

import java.net.MalformedURLException;
import java.net.SocketAddress;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import ci.web.HttpMethod;
import ci.web.codec.EmptyPostDecoder;
import ci.web.codec.FileItem;
import ci.web.codec.FormPostDecoder;
import ci.web.codec.JsonPostDecoder;

import com.alibaba.fastjson.JSONObject;

/**
 * ci-request-imp
 * @author zhh
 */
class CiRequestImp implements CiRequest {

    protected FullHttpRequest request;
    protected ChannelHandlerContext context;
    protected CiParamter params;
    protected FormPostDecoder decoder;
    private byte[] bodyBytes = null;
    protected HttpMethod method;
    
    public CiRequestImp(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
        this.request = req;
        this.context = ctx;
        this.method = HttpMethod.convert(req.method());
        
        if((method==HttpMethod.POST || method==HttpMethod.PUT) && req.content().readableBytes()>0){
            String type = contentType();
            JSONObject gets = query(req.uri(), new JSONObject(0), 64);
            if(type!=null && type.indexOf("json")>0){
                decoder = new JsonPostDecoder(req);
            }
            if(decoder==null){
                decoder = new FormPostDecoder(req);
                params = new CiParamter(decoder.paramters(), gets);
                if(params.isEmpty() && isJsonBody(req.content())){
                    try{
                        params = new CiParamter(new JsonPostDecoder(req).paramters(), gets);
                    }catch(Exception e){
                    }
                }
            }else{
                params = new CiParamter(decoder.paramters(), gets);
            }
        }else{
            decoder = EmptyPostDecoder.single();
            params = new CiParamter();
            query(req.uri(), params.get(), 64);
        }
    }

    private static boolean isJsonBody(ByteBuf buf){
        if(buf.readableBytes()<2)return false;
        char b = (char)((short)buf.getByte(buf.readerIndex()));
        char e = (char)((short)buf.getByte((buf.readerIndex()+buf.readableBytes()-1)));
        return (b=='{' && e=='}') || (b=='[' && e==']');
    }
    
    @Override
    public HttpMethod method() {
        return method;
    }
    private String _refererHost = "";
    @Override
    public String refererHost() {
        if(_refererHost==null||_refererHost.isEmpty()==false){
            return _refererHost;
        }
        String r = getHeader(HttpHeaderNames.REFERER);
        _refererHost = null;
        if(r==null||r.length()==0||r.equals("null")){
            return null;
        }
        try {
            URL u = new URL(r);
            _refererHost = u.getHost();
        } catch (MalformedURLException e) {
        }
        return _refererHost;
    }
    @Override
    public String host() {
        return getHeader(HttpHeaderNames.HOST);
    }
    @Override
    public String uri() {
        return request.uri();
    }
    @Override
    public String path() {
        int idx = uri().indexOf('?');
        if(idx>0){
            return uri().substring(0, idx);
        }
        return uri();
    }

    @Override
    public SocketAddress address() {
        return context.channel().remoteAddress();
    }

    @Override
    public JSONObject params() {
        return params;
    }
    @Override
    public JSONObject post() {
        return params.post();
    }
    @Override
    public JSONObject get() {
        return params.get();
    }
    @Override
    public byte[] body() {
        if(bodyBytes==null){
            byte[] bodyBytes = new byte[request.content().readableBytes()];
            request.content().getBytes(request.content().readerIndex(), bodyBytes);
        }
        return bodyBytes;
    }
    
    public List<FileItem> files(){
        return decoder.files();
    }
    @Override
    public FileItem getFile(String name){
        return decoder.getFile();
    }

    @Override
    public HttpHeaders headers() {
        return request.headers();
    }
    @Override
    public String getHeader(CharSequence name) {
        return headers().getAsString(name.toString());
    }
    
    private Set<Cookie> _cookies;
    @Override
    public Set<Cookie> cookies() {
        if(_cookies==null){
            String h = getHeader(HttpHeaderNames.COOKIE);
            _cookies = ServerCookieDecoder.STRICT.decode(h==null?"":h);
        }
        return _cookies;
    }
    @Override
    public Cookie getCookie(CharSequence name) {
        Set<Cookie> cs = cookies();
        for(Cookie c : cs){
            if(c.name().equals(name)){
                return c;
            }
        }
        return null;
    }
    @Override
    public String getCookieValue(CharSequence name) {
        Cookie c = getCookie(name);
        return c==null ? null:c.value();
    }
    
    @Override
    public String contentType() {
        return getHeader(HttpHeaderNames.CONTENT_TYPE);
    }
    
    @Override
    public boolean isFinish(){
        return decoder==null;
    }
    @Override
    public void finish(){
        if(decoder!=null){
            decoder.clean();
            decoder = null;
        }
    }
    /**
     * 解析uri参数
     * @param uri
     * @param maxParams 最大参数个数
     * @return
     */
    private static JSONObject query(String uri,JSONObject to, int maxParams){
        QueryStringDecoder decoder = new QueryStringDecoder(uri,HttpConstants.DEFAULT_CHARSET,true,maxParams);
        Map<String, List<String>> vars = decoder.parameters();
        Iterator<Entry<String, List<String>>> it = vars.entrySet().iterator();
        Entry<String, List<String>> entry;
        while(it.hasNext()){
            entry = it.next();
            if(entry.getValue().isEmpty()==false){
                to.putIfAbsent(entry.getKey(), entry.getValue().get(entry.getValue().size()-1));
            }
        }
        return to;
    }
    
}
